//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//                                                                              //
//  Author: meisq                                                               //
//          msq@qq.com                                                          //
//          ALINX(shanghai) Technology Co.,Ltd                                  //
//          heijin                                                              //
//     WEB: http://www.alinx.cn/                                                //
//     BBS: http://www.heijin.org/                                              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
// Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd                        //
//                    All rights reserved                                       //
//                                                                              //
// This source file may be used and distributed without restriction provided    //
// that this copyright statement is not removed from the file and that any      //
// derivative work contains the original copyright notice and the associated    //
// disclaimer.                                                                  //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

//================================================================================
//  Revision History:
//  Date          By            Revision    Change Description
//--------------------------------------------------------------------------------
//  2017/5/3     meisq          1.0         Original
//*******************************************************************************/
// 按钮去抖动模块
// 该模块用于去抖动按钮输入信号，防止因机械弹跳导致的误触发
// 参数:
// - N: 计数器位宽
// - FREQ: 系统时钟频率（MHz）
// - MAX_TIME: 去抖动时间（毫秒）
// 输入:
// - clk: 系统时钟
// - rst: 复位信号
// - button_in: 原始按钮输入信号
// 输出:
// - button_posedge: 按钮正沿输出信号
// - button_negedge: 按钮负沿输出信号
// - button_out: 去抖动后的按钮输出信号
module  ax_debounce 
(
    input       clk, 
    input       rst, 
    input       button_in,
    output reg  button_posedge,
    output reg  button_negedge,
    output reg  button_out
);

//// ---------------- 内部常量 --------------
parameter N = 32 ;           // 去抖动计数器位宽
parameter FREQ = 50;         // 系统时钟频率（MHz）
parameter MAX_TIME = 1;     // 去抖动时间（毫秒）
localparam TIMER_MAX_VAL =   MAX_TIME * 100 * FREQ;  // 计数器最大值

//// ---------------- 内部变量 ---------------
reg  [N-1 : 0]  q_reg;      // 计数寄存器
reg  [N-1 : 0]  q_next;     // 下一个计数值
reg DFF1, DFF2;             // 输入锁存器
wire q_add;                 // 控制标志
wire q_reset;               // 重置标志
reg button_out_d0;          // 延迟一个时钟周期的 button_out

//// 连续赋值用于计数器控制
assign q_reset = (DFF1  ^ DFF2);          // 异或输入锁存器以检测电平变化，用于重置计数器
assign q_add = ~(q_reg == TIMER_MAX_VAL); // 当计数器达到最大值时停止计数

//// 组合逻辑用于管理 q_next
always @ ( q_reset, q_add, q_reg)
begin
    case( {q_reset , q_add})
        2'b00 :
                q_next <= q_reg;          // 保持当前计数值
        2'b01 :
                q_next <= q_reg + 1;      // 计数器加一
        default :
                q_next <= { N {1'b0} };   // 重置计数器
    endcase     
end

//// 输入锁存器和 q_reg 更新
always @ ( posedge clk or posedge rst)
begin
    if(rst == 1'b1)
    begin
        DFF1 <= 1'b0;                     // 复位输入锁存器
        DFF2 <= 1'b0;
        q_reg <= { N {1'b0} };            // 复位计数寄存器
    end
    else
    begin
        DFF1 <= button_in;                // 锁存按钮输入
        DFF2 <= DFF1;                     // 锁存 DFF1 的值
        q_reg <= q_next;                  // 更新计数寄存器
    end
end

//// 计数器控制
always @ ( posedge clk or posedge rst)
begin
	if(rst == 1'b1)
		button_out <= 1'b1;              // 复位时设置 button_out 为高电平
    else if(q_reg == TIMER_MAX_VAL)
        button_out <= DFF2;              // 当计数器达到最大值时，更新 button_out
    else
        button_out <= button_out;        // 保持当前值
end

//// 边沿检测
always @ ( posedge clk or posedge rst)
begin
	if(rst == 1'b1)
	begin
		button_out_d0 <= 1'b1;           // 复位时设置 button_out_d0 为高电平
		button_posedge <= 1'b0;           // 复位时清除正沿检测
		button_negedge <= 1'b0;           // 复位时清除负沿检测
	end
	else
	begin
		button_out_d0 <= button_out;      // 延迟一个时钟周期的 button_out
		button_posedge <= ~button_out_d0 & button_out;  // 正沿检测
		button_negedge <= button_out_d0 & ~button_out;  // 负沿检测
	end	
end

endmodule

